//
//  HomeService.swift
//  home
//
//  Created by Yun Zeng on 2018/11/25.
//  Copyright © 2018 Yun Zeng. All rights reserved.
//

import Foundation

//
// 服务 Service
// 和配件的服务HMService关联，用户界面操作的的描述对象
// 包含用户名、房间、状态变化是否需要推送等信息
// 并不会每个HMService都关联Service
// 固件升级服务、设备描述信息等服务不在用户交互的范围内，因此不进行关联
//
class Service: Codable {
    // 服务状态
    enum State: String {
        case offline
        case online
        case update
        case highlight
    }
    var state: State = .online {
        didSet {
            self.manager?.addUpdatedServie(s: self)
        }
    }
    
    // aid和服务的sid 与 配件HMService对应
    var aid: String
    var sid: Int
    
    // 用户定制部分
    var name: String = ""
    var type: String
    var icon: String?
    var roomId: String?
    var notify: Bool = false
    var room: Room?
    
    // JSON格式化部分
    private enum CodingKeys: String, CodingKey {
        case aid
        case sid
        case name
        case type
        case icon
        case roomId = "room"
        case notify
    }
    
    // 关联部分
    var home: Home?
    var instance: HMService?
    var manager: ServiceManager?
    
    init?(service: HMService) {
        guard let aid = service.accessory?.id else {
            return nil
        }
        
        switch service.type {
        case .clock:
            self.icon = "clock"
        case .airConditioner:
            self.icon = "air"
        case .colorLight, .outlet, .switch, .temperatureColorLight:
            self.icon = "light"
        case .htSensor:
            self.icon = "sensor"
        case .contactSensor:
            self.icon = "door"
        case .motion:
            self.icon = "motion"
        case .zigbeeCoordinator:
            self.icon = "zigbee3"
        default:
            return nil
        }
        
        self.aid = aid
        self.sid = service.id
        self.type = service.type.rawValue
    }
    
    func findCharacteristic(type: HMCharacteristicType) -> HMCharacteristic? {
        guard let instance = self.instance else {
            return nil
        }
        for characteristic in instance.characteristics {
            if characteristic.type == type {
                return characteristic
            }
        }
        return nil
    }
}

// 调试打印
extension Service: CustomStringConvertible {
    var description: String {
        let data = try! JSONEncoder().encode(self)
        return String(data: data, encoding: .utf8)!
    }
}

extension Service: Hashable {
    func hash(into hasher: inout Hasher) {
        hasher.combine(self.aid)
        hasher.combine(self.sid)
    }
    
    static func == (lhs: Service, rhs: Service) -> Bool {
        if lhs.aid == rhs.aid && lhs.sid == rhs.sid {
            return true
        }
        return false
    }
}

// 服务管理
class ServiceManager: Codable {
    // 服务集合
    var home: Home?
    var services: [Service]
    init(services: [Service]) {
        self.services = services
        for service in self.services {
            service.manager = self
        }
    }
    
    // JSON 格式编解码
    public func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        try container.encode(self.services)
    }
    
    public required convenience init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        let service = try container.decode([Service].self)
        self.init(services: service)
    }
    
    // 与家庭进行关联
    func linkHome(home: Home, update: Bool = false) {
        self.home = home
        for service in self.services {
            service.instance = home.containerManager.linkService(surface: service)
            if let id = service.roomId {
                service.room = home.roomManager.findRoom(id: id)
            }
        }
        
        var needUpdate = false
        var services = [Service]()
        for service in self.services {
            if service.instance != nil {
                services.append(service)
            } else {
                Logger.Warning("delete service: \(service)")
                needUpdate = true
            }
        }
        self.services = services
        if let services = home.containerManager.findServiceUnconnected() {
            for service in services {
                if let surface = Service(service: service) {
                    needUpdate = true
                    surface.instance = service
                    service.surface = surface
                    surface.manager = self
                    Logger.Warning("add service: \(surface)")
                    self.services.append(surface)
                }
            }
        }
        
        if needUpdate {
            home.needUpdate()
        }
    }
    
    func findService(aid: String, sid: Int) -> Service? {
        for service in self.services {
            if service.aid == aid && service.sid == sid {
                return service
            }
        }
        return nil
    }
    
    // 有状态更新的服务集合
    var updates = Set<Service>()
    func addUpdatedServie(s: Service) {
        self.updates.insert(s)
    }
    
    // 监听家庭服务的变化
    var observers = [String: ServiceUpdateObserver]()
    func addObserver(identify: String, observer: ServiceUpdateObserver) {
        self.observers[identify] = observer
    }
    func removeObserver(identify: String) {
        self.observers.removeValue(forKey: identify)
    }
    
    // 将需要更新的服务发送给每一个监听者
    func refresh() {
        Logger.Info("updates <- \(self.updates.count)")
        if self.updates.count == 0 {
            return
        }
        for observer in observers {
            observer.value.update(services: self.updates)
        }
        self.updates.removeAll()
    }
}


// 执行动作
extension ServiceManager {
    func executeActions(actions: HomeAction...) -> Bool {
        return self.executeActions(actions: actions)
    }
    
    func executeActions(actions: [HomeAction]) -> Bool {
        let msg = CloudMessage(path: HomeMessagePath.characteristic.rawValue, method: .post, object: actions)
        msg.home = self.home?.id
        if CloudClient.instance.state != .transfer {
            return false
        }
        CloudClient.instance.sendMessage(msg: msg)
        return true
    }
}

// 服务状态变化观察
protocol ServiceUpdateObserver {
    func update(services: Set<Service>)
}
